package com.zhuojy.equity.mall.admin.manager.token;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.zhuojy.equity.mall.admin.common.constant.JwtConstants;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

/**
 * JwtToken生成的工具类 JWT token的格式：header.payload.signature header的格式（算法、token的类型）： {"alg": "HS512","typ": "JWT"}
 * payload的格式（用户名、创建时间、生成时间）： {"sub":"wang","created":1489079981393,"exp":1489684781} signature的生成算法：
 * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
 */
@Slf4j
@Component
public class TokenManager {

	private static final String CLAIM_KEY_USERNAME = "sub";

	private static final String CLAIM_KEY_CREATED = "created";

	/**
	 * 根据负载生成JWT的token
	 */
	private String generateToken(Map<String, Object> claims) {
		return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate())
				.signWith(SignatureAlgorithm.HS512, JwtConstants.SECRET).compact();
	}

	/**
	 * 从token中获取JWT中的负载
	 */
	private Claims  getClaimsFromToken(String token) {
		Claims claims = null;
		try {
			claims = Jwts.parser().setSigningKey(JwtConstants.SECRET).parseClaimsJws(token).getBody();
		} catch (Exception e) {
			log.info("JWT格式验证失败:{}", token);
		}
		return claims;
	}

	/**
	 * 生成token的过期时间
	 */
	private Date generateExpirationDate() {
		return new Date(System.currentTimeMillis() + JwtConstants.EXPIRATION * 1000);
	}

	/**
	 * 从token中获取登录用户名
	 */
	public String getUserNameFromToken(String token) {
		String username;
		try {
			Claims claims = getClaimsFromToken(token);
			username = claims.getSubject();
		} catch (Exception e) {
			e.printStackTrace();
			username = null;
		}
		return username;
	}

	/**
	 * 验证token是否还有效
	 *
	 * @param token       客户端传入的token
	 * @param userDetails 从数据库中查询出来的用户信息
	 */
	public boolean validateToken(String token, UserDetails userDetails) {
		String username = getUserNameFromToken(token);
		return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
	}

	/**
	 * 判断token是否已经失效
	 */
	private boolean isTokenExpired(String token) {
		Date expiredDate = getExpiredDateFromToken(token);
		return expiredDate.before(new Date());
	}

	/**
	 * 从token中获取过期时间
	 */
	private Date getExpiredDateFromToken(String token) {
		Claims claims = getClaimsFromToken(token);
		return claims.getExpiration();
	}

	/**
	 * 根据用户信息生成token
	 */
	public String generateToken(UserDetails userDetails) {
		Map<String, Object> claims = new HashMap<>();
		claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
		claims.put(CLAIM_KEY_CREATED, new Date());
		return generateToken(claims);
	}

	/**
	 * 当原来的token没过期时是可以刷新的
	 *
	 * @param oldToken 带tokenHead的token
	 */
	public String refreshHeadToken(String oldToken) {
		if (StrUtil.isEmpty(oldToken)) {
			return null;
		}
		String token = oldToken.substring(JwtConstants.TOKEN_HEAD.length());
		if (StrUtil.isEmpty(token)) {
			return null;
		}
		//token校验不通过
		Claims claims = getClaimsFromToken(token);
		if (claims == null) {
			return null;
		}
		//如果token已经过期，不支持刷新
		if (isTokenExpired(token)) {
			return null;
		}
		//如果token在30分钟之内刚刷新过，返回原token
		if (tokenRefreshJustBefore(token, 30 * 60)) {
			return token;
		} else {
			claims.put(CLAIM_KEY_CREATED, new Date());
			return generateToken(claims);
		}
	}

	/**
	 * 判断token在指定时间内是否刚刚刷新过
	 *
	 * @param token 原token
	 * @param time  指定时间（秒）
	 */
	private boolean tokenRefreshJustBefore(String token, int time) {
		Claims claims = getClaimsFromToken(token);
		Date created = claims.get(CLAIM_KEY_CREATED, Date.class);
		Date refreshDate = new Date();
		//刷新时间在创建时间的指定时间内
		return refreshDate.after(created) && refreshDate.before(DateUtil.offsetSecond(created, time));
	}
}
